package com.duowei.diandan.util;

import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.WorkerThread;
import android.util.Base64;
import android.util.Log;

import com.duowei.diandan.gson.LocalDateDeserializer;
import com.duowei.diandan.gson.LocalDateTimeDeserializer;
import com.duowei.diandan.gson.LocalTimeDeserializer;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;

import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okio.BufferedSink;
import okio.Okio;


public class NetUtils {
    private static final String TAG = "NetUtils";

    private static OkHttpClient sClient;
    private static Handler sHandler = new Handler();

    private NetUtils() {
    }

    public static void init() {
        if (sClient == null)
            sClient = new OkHttpClient();
    }

    public static Response get(String url) throws IOException {
        Request request = new Request.Builder()
                .url(url)
                .build();

        return sClient.newCall(request).execute();
    }

    public static void get(String url, GetListener listener) {
        Request request = new Request.Builder()
                .url(url)
                .build();

        sClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                sHandler.post(() -> {
                    if (listener != null) {
                        listener.onFail(e);
                    }
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String result = response.body().string();

                sHandler.post(() -> {
                    if (listener != null) {
                        listener.onSuccess(result);
                    }
                });
            }
        });
    }

    public static void post(String url, String state, String sql, Callback callback) {
        RequestBody body = new FormBody.Builder()
                .add("State", state)
                .add("Ssql", toBase64(sql))
                .build();

        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();

        sClient.newCall(request).enqueue(callback);
    }

    public static <T> void post6(String url, String sql, final Class<T> cls, final Post6Listener<T> listener) {
        post(url, "6", sql, new Callback() {
            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {
                sHandler.post(() -> {
                    if (listener != null) {
                        listener.onFail(e);
                    }
                });
            }

            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                final String result = response.body().string();
                sHandler.post(() -> {
                    if (listener != null) {
                        if (result.equals("]")) {
                            listener.onSuccess(Collections.emptyList());
                        } else {
                            try {
                                Gson gson = new GsonBuilder()
                                        .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeDeserializer())
                                        .registerTypeAdapter(LocalDate.class, new LocalDateDeserializer())
                                        .registerTypeAdapter(LocalTime.class, new LocalTimeDeserializer())
                                        .create();

                                Type type = TypeToken.getParameterized(ArrayList.class, cls).getType();
                                List<T> list1 = gson.fromJson(result, type);
                                listener.onSuccess(list1);
                            } catch (JsonSyntaxException e) {
                                Log.e(TAG, "JsonSyntaxException: ", e);
                                listener.onFail(e);
                            }
                        }
                    }
                });
            }
        });
    }

    public static void post7(String url, String sql, final Post7Listener listener) {
        post(url, "7", sql, new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                sHandler.post(() -> {
                    if (listener != null) {
                        listener.onFail(e);
                    }
                });
            }

            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                final String result = response.body().string();

                sHandler.post(() -> {
                    if (result.contains("richado")) {
                        if (listener != null) {
                            listener.onSuccess();
                        }
                    } else {
                        if (listener != null) {
                            listener.onFail(new Exception("网络请求结果没有包含richado"));
                        }
                    }
                });
            }
        });
    }

    // >> ------------------------------------------------------------------------------------------

    public static Response post(String url, String state, String sql) throws IOException {
        RequestBody body = new FormBody.Builder()
                .add("State", state)
                .add("Ssql", toBase64(sql))
                .build();

        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();

        return sClient.newCall(request).execute();
    }

    public static <T> List<T> post6(String url, String sql, final Class<T> cls) throws IOException {
        Response resp = post(url, "6", sql);
        String result = resp.body().string();
        if (resp.code() != 200) {
            throw new IOException("请检查网络连接");
        } else {
            if (result.equals("]")) {
                return Collections.emptyList();
            } else {
                try {
                    Gson gson = new GsonBuilder()
                            .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeDeserializer())
                            .registerTypeAdapter(LocalDate.class, new LocalDateDeserializer())
                            .registerTypeAdapter(LocalTime.class, new LocalTimeDeserializer())
                            .create();

                    Type type = TypeToken.getParameterized(ArrayList.class, cls).getType();
                    return gson.fromJson(result, type);
                } catch (JsonParseException e) {
                    throw new IOException(result);
                }
            }
        }
    }

    public static <T> List<T> post6(String url, String sql, final Class<T> cls, BodyListener listener) throws IOException {
        Response resp = post(url, "6", sql);
        String result = resp.body().string();
        if (listener != null) {
            listener.body(result);
        }

        if (result.equals("]")) {
            return Collections.emptyList();
        } else {
            try {
                Gson gson = new GsonBuilder()
                        .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeDeserializer())
                        .registerTypeAdapter(LocalDate.class, new LocalDateDeserializer())
                        .registerTypeAdapter(LocalTime.class, new LocalTimeDeserializer())
                        .create();

                Type type = TypeToken.getParameterized(ArrayList.class, cls).getType();
                return gson.fromJson(result, type);
            } catch (JsonParseException e) {
                throw new IOException("JSON解析错误, 网络返回结果: " + result);
            }
        }
    }

    /**
     * @param sql
     * @return true, 成功; false,没有这个,直接抛出IOException
     * @throws IOException
     */
    public static boolean post7(String url, String sql) throws IOException {
        String result = post(url, "7", sql).body().string();
        if (result.contains("richado")) {
            return true;
        }
        throw new IOException(result);
    }

    // << ------------------------------------------------------------------------------------------

    private static String toBase64(String sql) {
        return Base64.encodeToString(sql.replaceAll("\n", "").getBytes(), Base64.DEFAULT);
    }

    // -------------------------------------------
    // -------------------------------------------
    // -------------------------------------------

    @WorkerThread
    public static void downloadImage(String imageUrl, File savedFile) throws IOException {
        Request request = new Request.Builder()
                .url(imageUrl)
                .build();

        Response response = sClient.newCall(request).execute();

        if (response.code() == 200) {
            if (!savedFile.exists()) {
                //noinspection ResultOfMethodCallIgnored
                savedFile.createNewFile();
            }

            BufferedSink sink = Okio.buffer(Okio.sink(savedFile));
            sink.writeAll(response.body().source());
            sink.close();
        }
    }
}
